package com.wasion.pwportable.usb;

import android.content.Context;
import android.util.Log;

import com.ftdi.j2xx.D2xxManager;
import com.ftdi.j2xx.D2xxManager.D2xxException;
import com.ftdi.j2xx.FT_Device;
import com.wasion.core.config.Config;
import com.wasion.core.util.ConverterXBaseUtil;
import com.wasion.core.util.FrameFormatter;
import com.wasion.core.util.StringUtil;
import com.wasion.pwportable.domain.vo.DeviceNode;
import com.wasion.pwportable.protocol.Protocol;

public class F232DeviceManager {

	// 日志标记
	private static final String LOG_TAG = Config.LOG_TAG_GLOBOL;

	// 帧 等待时间
	private static int WAIT_AFTER_FIRST_ARRIVAL = 450;
	private static final int WAIT_FOR_DATA_AVALBLE = 100;
	private static final int WAIT_FOR_NEXT_BUFFER_LOAD = 20;
	// private static final int WAIT_FOR_ID_AVALBLE = 300;
	public static final int BAUDRATE = 2400;
	public static final int OPEN_INDEX = 0;

	private static Context parent = null;

	public D2xxManager d2xxManager;
	public FT_Device ftDev = null;

	private int devCount;
	private int currentIndex = -1;
	private int iavailable = 0;
	private boolean dataAval = false; // 从有读到数据开始，如果没有后续的数据要停止读buffer，输出到realData字符串

	private byte[] readData;
	public static final int readLength = 64;
	public volatile static String readDataStr;

	private static int iEnableReadFlag = 1;
	private static boolean bReadThreadGoing = false;
	public ReadThread read_thread;

	private static F232DeviceManager instance;

	public synchronized static F232DeviceManager getInstance(Context context) {
		if (instance == null) {
			instance = new F232DeviceManager(context);
		}
		return instance;
	}

	private F232DeviceManager(Context context) {
		parent = context;

		readData = new byte[readLength];

		try {
			d2xxManager = D2xxManager.getInstance(parent);
		} catch (D2xxException e) {
			e.printStackTrace();
		}
	}

	public DeviceNode getDeviceInformation() {

		DeviceNode node = new DeviceNode();

		try {
			devCount = 0;

			devCount = d2xxManager.createDeviceInfoList(parent);

			Log.i(LOG_TAG, "Device number = " + Integer.toString(devCount));
			if (devCount > 0) {
				D2xxManager.FtDeviceInfoListNode[] deviceList = new D2xxManager.FtDeviceInfoListNode[devCount];

				d2xxManager.getDeviceInfoList(devCount, deviceList);
				int normal = devCount;
				for (int i = devCount - 1; i >= 0; i--) {
					if (StringUtil.isEmpty(deviceList[i].description)
							|| StringUtil.isEmpty(deviceList[i].serialNumber)) {
						deviceList[i].description = "";
					} else {
						if (normal == devCount) {
							normal = i;
						} else {
							deviceList[i].description = "";
						}
					}
				}

				if (normal != devCount) {
					node.setName("FT232R");
					node.setSerialNumber(deviceList[normal].serialNumber);
					node.setId(deviceList[normal].id + "");
				} else {
					node.setId("disableId");
				}
			} else {
				node.setId("disableId");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return node;
	}

	public void enableRead() {

		iEnableReadFlag = (iEnableReadFlag + 1) % 2;

		ftDev.purge((byte) (D2xxManager.FT_PURGE_TX));

		if (iEnableReadFlag == 0) {
			ftDev.restartInTask();
		}
		// else {
		// ftDev.stopInTask();
		// Looper.prepare();
		// Toast.makeText(parent, "Device port is Read disabled",
		// Toast.LENGTH_LONG).show();
		// Looper.loop();
		// }
	}

	public boolean connect() {

		try {
			if (currentIndex != OPEN_INDEX) {
				if (null == ftDev) {
					ftDev = d2xxManager.openByDescription(parent,
							"FT232R USB UART");
				} else {
					synchronized (ftDev) {
						ftDev = d2xxManager.openByDescription(parent,
								"FT232R USB UART");
					}
				}
			} else {
				return true;
			}

			if (ftDev == null) {
				return false;
			}

			boolean status = ftDev.isOpen();
			if (status) {
				currentIndex = OPEN_INDEX;

				if (false == bReadThreadGoing) {
					read_thread = new ReadThread();
					read_thread.start();
					bReadThreadGoing = true;
				}
			}
			return status;
		} catch (Exception e) {
			e.printStackTrace();
			return false;
		}

	}

	public void disconnect() {

		devCount = -1;
		currentIndex = -1;
		bReadThreadGoing = false;

		try {
			Thread.sleep(50);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		try {
			if (ftDev != null) {
				synchronized (ftDev) {
					if (true == ftDev.isOpen()) {
						ftDev.close();
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void setConfig() {

		if (null == ftDev || !ftDev.isOpen()) {
			Log.e(LOG_TAG, "setConfig: device not open");
			return;
		}

		ftDev.setBaudRate(BAUDRATE);
		byte stopBits = D2xxManager.FT_STOP_BITS_1;
		byte dataBits = D2xxManager.FT_DATA_BITS_8;
		byte parity = D2xxManager.FT_PARITY_NONE;

		ftDev.setDataCharacteristics(dataBits, stopBits, parity);
	}

	public void startReadThread() {
		if (null != ftDev && ftDev.isOpen()) {
			if (false == bReadThreadGoing) {
				bReadThreadGoing = true;
				read_thread = new ReadThread();
				read_thread.start();
			}
		}
	}

	private class ReadThread extends Thread {
		@Override
		public void run() {

			StringBuffer realData = new StringBuffer();
			try {
				while (bReadThreadGoing) {
					try {
						Thread.sleep(WAIT_FOR_NEXT_BUFFER_LOAD);
					} catch (InterruptedException e) {
					}

					if (null != ftDev) {
						synchronized (ftDev) { // 这里应该是个同步的阻塞方式读

							iavailable = ftDev.getQueueStatus();
							if (iavailable > 0) {
								dataAval = true;

								if (iavailable > readLength) {
									iavailable = readLength;
								}

								ftDev.read(readData, iavailable);

								realData.append(ConverterXBaseUtil
										.bytesToHexString(readData, iavailable));
								readDataStr = realData.toString();

								WAIT_AFTER_FIRST_ARRIVAL = 650;
								if (4 <= readDataStr.length()) {
									if ("7E".equalsIgnoreCase(readDataStr
											.substring(0, 2))) {

										int len = Integer
												.parseInt(readDataStr
														.substring(2, 4), 16);
										if (10 > len) {
											WAIT_AFTER_FIRST_ARRIVAL = 100;
										} else if (10 <= len && 30 > len) {
											WAIT_AFTER_FIRST_ARRIVAL = 200;
										} else {
											WAIT_AFTER_FIRST_ARRIVAL = 450;
										}
									}
								}
							} else {
								if (dataAval == true) {

									readDataStr = realData.toString();
									// ftDev.purge((byte)
									// D2xxManager.FT_PURGE_RX);
									// //
									// dataAval
									// =
									// false;
									// bReadThreadGoing = false;
								}

							}
						}
					}
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}

	public void sendMessage(String writeData) {
		try {
			if (null == ftDev || !ftDev.isOpen()) {
				Log.e(LOG_TAG, "SendMessage: device not open");
				return;
			}

			ftDev.setLatencyTimer((byte) 16);
			// ftDev.purge((byte) (D2xxManager.FT_PURGE_TX |
			// D2xxManager.FT_PURGE_RX));

			String input = FrameFormatter.deformat(writeData);

			byte[] outData = ConverterXBaseUtil.hexStringToByteArray(input);
			ftDev.write(outData, outData.length);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	// 普通的发送接收 处理
	public String sendAndRead(String input) {
		try {
			readDataStr = "";

			enableRead();
			startReadThread();
			sendMessage(input);

			// sendMessage(input);
			while (dataAval != true) {
				try {
					Thread.sleep(WAIT_FOR_DATA_AVALBLE);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}

			// System.err.println("++++++++++++++++++++++++ "
			// + WAIT_AFTER_FIRST_ARRIVAL);
			// 再等100ms,等全部数据被处理完
			try {
				Thread.sleep(WAIT_AFTER_FIRST_ARRIVAL);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}

			while (true) {
				if (readDataStr.length() > 2) {
					if ("16".equals(readDataStr.substring(readDataStr.length() - 2))) {
						break;
					}
				}
				try {
					Thread.sleep(WAIT_FOR_NEXT_BUFFER_LOAD);
				} catch (InterruptedException e) {
				}
			}
			dataAval = false;
			// ftDev.purge((byte) D2xxManager.FT_PURGE_RX);
			bReadThreadGoing = false;

			// System.err.println("+++++++++++++++++++++1 " + readDataStr);
			// 丢弃错误帧
			if (!StringUtil.isEmpty(readDataStr)) {
				if (!"7E".equalsIgnoreCase(readDataStr.substring(0, 2))) {
					readDataStr = "";
				} else {

					int headerAndFootLength = 0;
					int flen = Integer
							.parseInt(readDataStr.substring(2, 4), 16);
					if (flen <= 4) {
						headerAndFootLength = 4;
					} else {
						int multiLevel = Integer.parseInt(
								readDataStr.substring(14, 16), 16);
						headerAndFootLength = multiLevel > 1 ? 2 : 4;
					}

					int clen = readDataStr.length() / 2 - headerAndFootLength;
					// System.err.println("+++++++++++" + clen + " " + flen);
					if (clen >= flen) {
						readDataStr = readDataStr.substring(0,
								(flen + headerAndFootLength) * 2);
					} else {
						readDataStr = "";
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		// System.err.println("+++++++++++++++++++++2 " + readDataStr);
		return readDataStr;

	}

	public String getDeiveCommAddr() {
		String deviceId = "";
		try {
			sendAndRead(Protocol.AddressQurey_Code);
			/**
			 * enableRead(); startReadThread();
			 * 
			 * sendMessage(Protocol.AddressQurey_Code);
			 * 
			 * try { Thread.sleep(WAIT_FOR_ID_AVALBLE); } catch
			 * (InterruptedException e) { e.printStackTrace(); }
			 **/

			if (null != readDataStr && readDataStr.length() >= 12) {
				deviceId = readDataStr.substring(10, 12)
						+ readDataStr.substring(8, 10)
						+ readDataStr.substring(6, 8);
				readDataStr = "";
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return deviceId;
	}
}
